﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Djs.Games.DeskGame.Forms
{
    public partial class LayersTestForm : Form
    {
        public LayersTestForm()
        {
            InitializeComponent();
            this.ImageLibrary = new GameComponents.GameImageLibrary();

            this._LayerGraph.ImageBackA = this.ImageLibrary["development-database.png"];
            this._LayerGraph.ImageBackB = this.ImageLibrary["development-java.png"];
            this._LayerGraph.ImageForeA = this.ImageLibrary["animals-rabbit.png"];
            this._LayerGraph.ImageForeB = this.ImageLibrary["animals-rabbit-shadow.png"];
            this._LayerGraph.IsPrepared = true;
            this._LayerGraph.PaintDone += new EventHandler<PaintDoneEventArgs>(_PaintDone);

            this._StandardGraph.ImageBackA = this.ImageLibrary["development-database.png"];
            this._StandardGraph.ImageBackB = this.ImageLibrary["development-java.png"];
            this._StandardGraph.ImageForeA = this.ImageLibrary["animals-rabbit.png"];
            this._StandardGraph.ImageForeB = this.ImageLibrary["animals-rabbit-shadow.png"];
            this._StandardGraph.IsPrepared = true;
            this._StandardGraph.PaintDone += new EventHandler<PaintDoneEventArgs>(_PaintDone);

            this._MethodLayer.Checked = true;

            this._LayerGraph.LayerCount = 2;
        }
        private Djs.Games.DeskGame.GameComponents.GameImageLibrary ImageLibrary;
        private void _MethodChanged(object sender, EventArgs e)
        {
            this._LastTimes = new List<double>();

            if (this._MethodStandard.Checked)
            {
                this._LayerGraph.Visible = false;

                this._StatusText.Text = "Control je vykreslován standardní technikou Windows: v eventu Control.Paint se kreslí přímo na plochu controlu.";
                this._StandardGraph.Bounds = this._LayerGraph.Bounds;
                this._StandardGraph.Visible = true;
            }
            else
            {
                this._StandardGraph.Visible = false;

                bool isLayered = this._MethodLayer.Checked;
                if (!isLayered)
                {
                    this._LayerGraph.MouseMoveDrawLayer = 0;
                    this._StatusText.Text = "Control je vykreslován pomocí bufferované grafiky: je nevizuálně vykreslen do bufferu jen v případě potřeby, a pak přenesen do Controlu. Překresluje se vždy celý obsah.";
                }
                else
                {
                    this._LayerGraph.MouseMoveDrawLayer = 1;
                    this._StatusText.Text = "Control je vykreslován pomocí vrstvové grafiky: má více vrstev bufferu, a kreslí se jen vrstvy se změnou. Do Controlu se z bufferu přenáší v případě potřeby okamžitě.";
                }
                this._LayerGraph.Visible = true;
            }
            this._LastTimes = new List<double>();
        }
        private void _LayerGraph_SizeChanged(object sender, EventArgs e)
        {
            this._StandardGraph.Bounds = this._LayerGraph.Bounds;
        }
        void _PaintDone(object sender, PaintDoneEventArgs e)
        {
            if (this._LastTimes == null)
                this._LastTimes = new List<double>();
            if (this._LastTimes.Count > 8)
                this._LastTimes.RemoveAt(0);
            this._LastTimes.Add(e.Time);
            double avg = this._LastTimes.Average();          // Průměrný čas v milisekundách na jeden běh
            double fps = Math.Round(1000d / avg, 2);
            this._StatusFps.Text = fps.ToString("## ##0.00").Trim() + " FPS";
            this.statusStrip1.Refresh();
        }
        private List<double> _LastTimes;
    }
    public class PaintDoneEventArgs : EventArgs
    {
        public PaintDoneEventArgs(double time)
            : base()
        {
            this.Time = time;
        }
        public double Time { get; private set; }
    }
    /// <summary>
    /// Třída, která reprezentuje (volitelné) používání LayeredPanel (vrstvové grafiky)
    /// </summary>
    public class TestLayerGraph : Djs.WinForm.Components.LayeredPanel
    {
        public TestLayerGraph()
        {
            this.LayerCount = 2;
            this.MouseEnter += new EventHandler(_MouseEnter);
            this.MouseMove += new MouseEventHandler(_MouseMove);
            this.MouseLeave += new EventHandler(_MouseLeave);
        }
        void _MouseEnter(object sender, EventArgs e)
        {
            
        }
        void _MouseMove(object sender, MouseEventArgs e)
        {
            this._MousePoint = e.Location;
            this.ReDraw(this.MouseMoveDrawLayer);
        }
        void _MouseLeave(object sender, EventArgs e)
        {
            this._MousePoint = null;
            this.ReDraw(this.MouseMoveDrawLayer);
        }
        /// <summary>
        /// Layer který se má překreslovat při pohybech myši
        /// </summary>
        public int MouseMoveDrawLayer { get; set; }
        public Image ImageBackA;
        public Image ImageBackB;
        public Image ImageForeA;
        public Image ImageForeB;
        public bool IsPrepared;
        public event EventHandler<PaintDoneEventArgs> PaintDone;
        /// <summary>
        /// Bod myši, na němž se má vykreslovat obrázek
        /// </summary>
        private Point? _MousePoint;
        protected override void OnPaintLayers(WinForm.Components.LayeredPaintEventArgs e)
        {
            base.OnPaintLayers(e);
            if (!this.IsPrepared) return;

            System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();

            if (e.HasPaintLayer(0))
            {
                TestDraw.DrawBack(e.GraphicsForLayer(0), this.ClientRectangle, this.ImageBackA, this.ImageBackB);
            }

            if (e.HasPaintLayer(0) || e.HasPaintLayer(1))
            {
                e.CopyContentOfLayer(0, 1);
                if (this._MousePoint.HasValue)
                    TestDraw.DrawImage(e.GraphicsForLayer(1), this._MousePoint.Value, this.ImageForeB, this.ImageForeA);
            }

            sw.Stop();
            if (this.PaintDone != null)
            {
                double time = 1000d * (double)sw.ElapsedTicks / (double)System.Diagnostics.Stopwatch.Frequency;
                this.PaintDone(this, new PaintDoneEventArgs(time));
            }
        }
    }
    /// <summary>
    /// Třída, která reprezentuje Windows-style používání grafiky (přímý Paint)
    /// </summary>
    public class TestStandardPanel : System.Windows.Forms.Panel
    {
        public TestStandardPanel()
        {
            this.MouseEnter += new EventHandler(_MouseEnter);
            this.MouseMove += new MouseEventHandler(_MouseMove);
            this.MouseLeave += new EventHandler(_MouseLeave);

            this.Paint += new PaintEventHandler(_Paint);
            this.ResizeRedraw = true;
        }
        void _MouseEnter(object sender, EventArgs e)
        {

        }
        void _MouseMove(object sender, MouseEventArgs e)
        {
            this._MousePoint = e.Location;
            this.Invalidate();
        }
        void _MouseLeave(object sender, EventArgs e)
        {
            this._MousePoint = null;
            this.Invalidate();
        }
        public Image ImageBackA;
        public Image ImageBackB;
        public Image ImageForeA;
        public Image ImageForeB;
        public bool IsPrepared;
        public event EventHandler<PaintDoneEventArgs> PaintDone;
        /// <summary>
        /// Bod myši, na němž se má vykreslovat obrázek
        /// </summary>
        private Point? _MousePoint;
        void _Paint(object sender, PaintEventArgs e)
        {
            if (!this.IsPrepared) return;

            System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();

            // Background
            TestDraw.DrawBack(e.Graphics, this.ClientRectangle, this.ImageBackA, this.ImageBackB);

            // Image
            if (this._MousePoint.HasValue)
                TestDraw.DrawImage(e.Graphics, this._MousePoint.Value, this.ImageForeB, this.ImageForeA);

            sw.Stop();
            if (this.PaintDone != null)
            {
                double time = 1000d * (double)sw.ElapsedTicks / (double)System.Diagnostics.Stopwatch.Frequency;
                this.PaintDone(this, new PaintDoneEventArgs(time));
            }

        }
    }
    public static class TestDraw
    {
        public static void DrawBack(Graphics graphic, Rectangle bounds, Image imageA, Image imageB)
        {
            if (graphic == null || imageA == null || imageB == null) return;
            Size sizeA = imageA.Size;
            Size sizeB = imageB.Size;
            int spaceX = sizeA.Width + sizeB.Width;
            int spaceY = sizeA.Height + sizeB.Height;
            int offsAx = 0;
            int offsAy = 0;
            int offsBx = sizeA.Width;
            int offsBy = sizeA.Height;
            for (int y = bounds.Top; y < bounds.Bottom; y += spaceY)
            {
                for (int x = bounds.Left; x < bounds.Right; x += spaceX)
                {
                    RectangleF rA = new RectangleF(new Point(x + offsAx, y + offsAy), sizeA);
                    RectangleF rB = new RectangleF(new Point(x + offsBx, y + offsBy), sizeB);
                    graphic.DrawImage(imageA, rA);
                    graphic.DrawImage(imageB, rB);
                }
            }
        }
        public static void DrawImage(Graphics graphics, Point point, Image imageBack, Image imageFore)
        {
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

            Size sizeBack = imageBack.Size;
            Rectangle areaBack = new Rectangle(new Point(point.X - sizeBack.Width / 2 + 3, point.Y - sizeBack.Height / 2 + 1), sizeBack);
            graphics.DrawImage(imageBack, areaBack);

            Size sizeFore = imageFore.Size;
            Rectangle areaFore = new Rectangle(new Point(point.X - sizeFore.Width / 2, point.Y - sizeFore.Height / 2), sizeFore);
            graphics.DrawImage(imageFore, areaFore);
        }
    }
}
